import java.awt.Color;
import java.awt.DisplayMode;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Toolkit;
import java.awt.image.BufferStrategy;
 
import javax.swing.JFrame;
 
public class test2
    implements Runnable {
 
    static final int APP_WIDTH = 640;
    static final int APP_HEIGHT = 480;
 
    private GraphicsEnvironment mGraphicsEnv = null;
    private GraphicsDevice mGraphicsDev = null;
    private GraphicsConfiguration mGraphicsConf = null;
    private BufferStrategy mBufferStrategy = null;
 
    private JFrame mFrame = null;
 
    private int targetFps;
     
    // changed box width to 1, so tearing is much more obvious.
    public static final int BOX_WIDTH = 1;
    public static final int BOX_HEIGHT = 300;
     
    private int mBoxX = 200;
    private int mBoxY = 200;
    private int mBoxDX = 1;
    private int mBoxDY = 1;
 
    test2() {
        mGraphicsEnv = GraphicsEnvironment.getLocalGraphicsEnvironment();
        mGraphicsDev = mGraphicsEnv.getDefaultScreenDevice();
        mGraphicsConf = mGraphicsDev.getDefaultConfiguration();
         
        final int currentRefreshrate = mGraphicsDev.getDisplayMode().getRefreshRate();
        // if the refresh rate is unknown, we hope 60 is ok.
        // Alternatively, you could switch to fullscreen mode for a few second, and attempt to use the vsync lock of a
        // page flipping BufferStrategy to determine the refreshrate.
        System.out.println("Reported Refreshrate=" + currentRefreshrate);
        targetFps = (currentRefreshrate==DisplayMode.REFRESH_RATE_UNKNOWN?60:currentRefreshrate);
 
        mFrame = new JFrame(mGraphicsConf);
        mFrame.setIgnoreRepaint(true);
        mFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        mFrame.setLocation(100, 100);
        mFrame.setSize(APP_WIDTH, APP_HEIGHT);
        mFrame.setFocusTraversalKeysEnabled(false);
        mFrame.setResizable(false);
        mFrame.setVisible(true);
 
        mFrame.createBufferStrategy(2);
        mBufferStrategy = mFrame.getBufferStrategy();
 
        Thread thread = new Thread(this);
        thread.start();
    }
 
    public void run() {
         
        long startTime = System.nanoTime();
        long frameCount = 0;
         
        while(true) {
 
            frameCount++;
 
            // this isn't needed for timing
            // I just use it to gauge how much drawing can be done in the frame
            // without over-running the frameEndTime. (which would cause tearing)
            final long frameStartTime = System.nanoTime();
 
            // time at which this frame should end so it doesn't run over the sync-time
            final long frameEndTime = startTime+(frameCount*1000000000)/targetFps;
             
            mBoxX += mBoxDX;
            mBoxY += mBoxDY;
 
            // this was incorrect - it was '>', where-as it should be '>=' 
            if(mBoxX >= APP_WIDTH - BOX_WIDTH)
                mBoxDX = -1;
            if(mBoxX < 0)
                mBoxDX = 1;
            // this was incorrect - it was '>', where-as it should be '>=' 
            if(mBoxY >= APP_HEIGHT - BOX_HEIGHT)
                mBoxDY = -1;
            if(mBoxY < 0)
                mBoxDY = 1;
 
            Graphics2D graphics = (Graphics2D)mBufferStrategy.getDrawGraphics();
 
            // Vary the amount of painting work.
            // This makes the game loop more representative of how a real game would behave.
            // and better demonstrates why the bufferStrategy.show() should be placed
            // after the timing loop, not before it.            
            final long paintEndTime = frameStartTime + (long)((frameEndTime-frameStartTime)*0.8*Math.random());
            while(System.nanoTime()<paintEndTime) {
                graphics.clearRect(0, 0, APP_WIDTH, APP_HEIGHT);
                // Toolkit sync() is needed to stop the Java2D pipeline buffering these commands
                // (which can cause an uncontrollable oscillation in the number of draws.)
                // Try taking this out, and run the app. a few times.
                // You will see a very bizarre effect occurring!  
                Toolkit.getDefaultToolkit().sync();
            }
            graphics.setColor(Color.RED);
            graphics.fillRect(mBoxX, mBoxY, BOX_WIDTH, BOX_HEIGHT);
 
            graphics.dispose();
 
//          mBufferStrategy.show(); // alternate (bad) place to have the show()
             
            // timing loop - you do not want to do this when vsync is supported (in fullscreen exclusive mode)
            // as it will interfere with the synchronisation performed by the vsync.
            while(System.nanoTime()<frameEndTime) {
                Thread.yield();
            }
            // do the show() after the timing loop,
            // so the copy to the screen buffer occurs at approx. the same time each frame.
             
            // if you did it before the timing loop, the time at which the copy occurs
            // will vary according to how busy the game loop is each frame - this will cause excessive tearing.
 
            // To see what I mean, try moving it above the timing loop
            // The fluctuations will dramatically increase the amount of tearing.
            mBufferStrategy.show();
        }
    }
 
    public static void main(String[] args) {
        new test2();
    }
}